Skip to content

fix(asr): repair ConvSubsampling forward paths missed by the MaskedConvSequential refactor - #16225

Open
ManoharPaturi wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
ManoharPaturi:fix/convsampling-missing-lengths-arg
Open

ManoharPaturi wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
ManoharPaturi:fix/convsampling-missing-lengths-arg

Conversation

@ManoharPaturi

Copy link
Copy Markdown

Fixes #16223.

Three repairs to paths the MaskedConvSequential refactor (#13827) missed:

  • MaskedConvSequential.forward takes optional lengths; 1-D stacks without length-aware layers just pass them through
  • ConvSubsampling.forward now forwards lengths to self.conv(...) in the non-chunked / conv1d branch (the else that previously called self.conv(x))
  • _layer_padding handles nn.MaxPool2d storing int kernel/stride/padding (via a _pair_first helper) and accounts for the extra frame ceil_mode=True can add

subsampling_conv_chunking_factor=-1 verified bit-identical to the default path for striding/dw_striding/vggnet at factors 4 and 8. 14 new CPU tests — all fail on main, pass here.

…nvSequential refactor

The MaskedConvSequential refactor left three ConvSubsampling paths broken:

1. `subsampling_conv_chunking_factor=-1` (chunking disabled, documented in the
   class docstring) falls into `x, lengths = self.conv(x)`, which calls
   MaskedConvSequential.forward without the required `lengths` argument and
   raises TypeError for every conv2d stack (vggnet, striding, dw_striding).

2. The 1-D stacks (striding_conv1d, dw_striding_conv1d, conv2d_subsampling=False)
   always take that same branch regardless of the chunking factor, so both
   variants raise TypeError on every forward pass.

3. For vggnet, `_forward_torch` reads `layer.kernel_size[0]` / `layer.stride[0]`,
   but nn.MaxPool2d stores the int values passed in (kernel_size=2, stride=2),
   so every vggnet forward raises TypeError: 'int' object is not subscriptable.
   _layer_padding has the same problem with the int `padding`. Once the int
   access works, the length update must also honor MaxPool2d's ceil_mode=True,
   which calculate_conv_output_size's floor division ignores.

Fixes:
- ConvSubsampling.forward: pass `lengths` to the masked stack when chunking is
  disabled; run 1-D stacks as a plain sequential (MaskedConvSequential.forward
  accepts lengths=None for exactly this) and report the precomputed out_lengths.
- _forward_torch: read kernel/stride/padding via a _pair_first helper that
  tolerates int attributes, and add the ceil-mode remainder frame when a
  pooling layer runs with ceil_mode=True.

Adds CPU unit tests for all three paths; each fails with TypeError or wrong
lengths on the unfixed code. The chunking=-1 path is asserted to match the
default (chunked) path bit-for-bit, and conv1d/vggnet lengths are asserted
against calc_length references.

Signed-off-by: Manohar Paturi <186662190+ManoharPaturi@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 7, 2026 07:11
@copy-pr-bot

copy-pr-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added the ASR label Sep 7, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 9, 2026
@pzelasko

Copy link
Copy Markdown
Collaborator

/ok to test 738768e

@pzelasko

Copy link
Copy Markdown
Collaborator

@codex review and check for potential for silent regressions when the caller doesn't provide lengths argument, but they should have

@pzelasko
pzelasko self-requested a review September 14, 2026 14:36
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T14:41:35.368743Z 738768e Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 738768e608

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

x, lengths = self.conv(x, lengths)
else:
# 1-D conv stacks run without masking; keep the lengths computed above.
x = self.conv(x)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve length masking in the restored 1-D path

For striding_conv1d and dw_striding_conv1d with variable-length batches, this call deliberately omits lengths, so the new lengths=None branch processes padded frames as real input. With the symmetric stride-2 convolutions, padded values can affect even the final frame inside out_lengths (for example, the last valid output for length 97 and kernel size 5 reads positions beyond 96), silently making model results depend on batch padding. The new tests assert only shapes and calculated lengths, so they do not detect this; implement length-aware mask propagation for the 1-D layers and test invariance to changes beyond each sample's length.

AGENTS.md reference: AGENTS.md:L72-L78

Useful? React with 👍 / 👎.

fuse_triton = False

def forward(self, x, lengths):
def forward(self, x, lengths=None):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add an author-matching DCO sign-off

Commit 1ed03a40f2fae9ab5a42146e4e902acfae763e47 has no Signed-off-by trailer, so it cannot be merged under the repository's DCO policy. Configure user.name and user.email to the real author identity, then repair the branch with git rebase --signoff origin/main and git push --force-with-lease.

AGENTS.md reference: AGENTS.md:L80-L90

Useful? React with 👍 / 👎.

@ManoharPaturi

Copy link
Copy Markdown
Author

good question, checked it before making the change:

  • ConvSubsampling.forward(self, x, lengths) still takes lengths as a required positional arg, so a caller who should pass it but doesn't gets the same loud TypeError as before. that contract is unchanged.

  • the optional part is only on the internal MaskedConvSequential.forward(x, lengths=None). None means plain nn.Sequential pass-through, and that branch is only reachable from the 1-D stacks (striding_conv1d / dw_striding_conv1d), where no layer consumes lengths at all and ConvSubsampling computes out_lengths upfront with calc_length, same as the pre-Improve ASR models' invariance to padding/batch size #13827 code did.

  • the 2-D / vggnet path always passes lengths explicitly. and if someone calls the wrapper directly with lengths=None on a 2-D stack, it returns a single tensor instead of a tuple, so the misuse is loud (unpack error), not silent.

so the silent-regression case would need someone to call the internal wrapper directly, skip lengths, and use the single-return contract, which is exactly the documented plain-pass-through mode. happy to make the wrapper raise on None for 2-D stacks instead if you'd rather have that belt and suspenders.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Sep 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

[🤖]: Hi @ManoharPaturi 👋,

We wanted to let you know that a CICD pipeline for this PR just finished successfully.

So it might be time to merge this PR or get some approvals.

@ManoharPaturi

Copy link
Copy Markdown
Author

Hi @pzelasko, following up as the full CICD pipeline has completed successfully. Please let me know if any further changes are needed or if this is good to merge. Thanks!

@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ConvSubsampling forward broken: chunking=-1, striding_conv1d/dw_striding_conv1d, and vggnet all raise TypeError

4 participants